using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using MySql.Data.MySqlClient;

namespace Cz.CzDbMySql
{
    public class CzDbMySql : ICzDb
    {
        [CzServiceField] private ICzLog log;
        [CzServiceField] private ICzConfig config;

        private string con;
        private bool debug;
        private readonly object sqlLock = new object();
        
        private MySqlConnection mysql;
        
        public void Start()
        {
            con = config.Get<string>("mysql", "con");
            ReLoadConfig();

            config.OnChange += ReLoadConfig;
            
            mysql = new MySqlConnection(this.con);
            mysql.Open();
            
            log.Info("CzDbMySQL start", "con:" + con);
        }

        public void Close()
        {
            log.Info("CzDbMySQL Close");
            mysql?.Close();
        }

        public int Exec(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            lock (sqlLock)
            {
                return sqlCommand.ExecuteNonQuery();
            }
            
        }

        public string ExecString(string sql, params DbParameter[] parameters)
        {
            var obj = ExecObject(sql, parameters);
            return obj?.ToString();
        }
        
        public int? ExecInt(string sql, params DbParameter[] parameters)
        {
            var obj = ExecObject(sql, parameters);
            return obj == null ? null : (int?)Convert.ToInt32(obj);
        }
        
        public object ExecObject(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            lock (sqlLock)
            {
                return sqlCommand.ExecuteScalar();
            }
        }
        
        public Hashtable ExecRow(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            Hashtable ret = null;
            
            lock (sqlLock)
            {
                var reader = sqlCommand.ExecuteReader();
                if (reader.Read())
                {
                    ret = new Hashtable();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        ret[reader.GetName(i)] = reader[i];
                    }
                }

                reader.Close();
            }

            return ret;
        }
        
        public List<Hashtable> ExecTable(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            var array = new LinkedList<Hashtable>();
            
            lock (sqlLock)
            {
                var reader = sqlCommand.ExecuteReader();
                while (reader.Read())
                {
                    var ht = new Hashtable();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        ht[reader.GetName(i)] = reader[i];
                    }

                    array.AddLast(ht);
                }

                reader.Close();
            }

            var ret = new List<Hashtable>(array.Count);
            ret.AddRange(array);
            return ret;
        }

        public DbParameter CreatePara(string name, object val)
        {
            return new MySqlParameter(name, val);
        }

        private MySqlCommand CreateCommand(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = new MySqlCommand(sql, mysql);
            sqlCommand.Parameters.AddRange(parameters);
            
            if(debug)
                log.Info("mysql exec", sqlCommand.CommandText);
            
            return sqlCommand;
        }

        private void ReLoadConfig()
        {
            debug = config.GetWithDefault(false, "mysql", "debug");
        }

        public void Dispose()
        {
            Close();
            mysql?.Dispose();
        }
    }
}